package com.chif.goingplus.aop.log;

import com.chif.goingplus.aop.log.service.LogNoteService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 统一接口日志处理(日志入库)
 * @author liudachu
 */
@Aspect
@Component
public class LogAspect {

    /**
     * 日志集合，用于存储不同线程下的LogNote(日志记录)
     */
    private static Map<String, com.chif.goingplus.aop.log.pojo.LogNote> logNoteMap = new ConcurrentHashMap<>();

    @Resource
    LogNoteService logNoteService;

    /**
     * 定义切点，切所有的Controller中的接口方法
     */
    @Pointcut("execution(public * com.chif.goingplus.controller.*.*(..))")
    public void LogAspect() {
    }

    /**
     * 在目标方法完全执行后（return后）再执行
     * @param joinPoint
     * @param data
     */
    @AfterReturning(value = "LogAspect()", returning = "data")
    public void doAfterReturning(JoinPoint joinPoint, Object data) {
        logNote(joinPoint, data);
    }

    /**
     * 包裹目标方法
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("LogAspect()")
    public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Method methodSignature = ((MethodSignature) joinPoint.getSignature()).getMethod();
        LogNote loggerInfo = methodSignature.getAnnotation(LogNote.class);
        if (loggerInfo != null) {
            //记录接口执行信息
            Thread.currentThread().setName(UUID.randomUUID().toString());
            com.chif.goingplus.aop.log.pojo.LogNote logNote = new com.chif.goingplus.aop.log.pojo.LogNote();
            logNote.setInterfaceRequestTime(System.currentTimeMillis());
            logNote.setInterfaceName(loggerInfo.value());
            logNoteMap.put(Thread.currentThread().getName(), logNote);
        }
        Object obj;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            //持久化异常日志
            logNote(joinPoint, e);
            e.printStackTrace();
            throw e;
        }
        return obj;
    }


    /**
     * 日志记录(持久化)
     *
     * @param joinPoint
     * @param data
     * @param e
     */
    private void logNote(JoinPoint joinPoint, Object data, Throwable e) {
        if (!logNoteMap.containsKey(Thread.currentThread().getName())) {
            //当前线程无日志记录，结束执行该持久化操作
            return;
        }
        //持久化日志到数据库，内存(map集合)中剔除该日志
        com.chif.goingplus.aop.log.pojo.LogNote logNote = logNoteMap.get(Thread.currentThread().getName());
        logNote.setInterfaceError(e == null ? "" : getErrorMsg(e));
        logNoteMap.remove(Thread.currentThread().getName());
        logNoteService.logInsert(joinPoint, data, logNote);

    }

    /**
     * 获取异常信息打印日志
     * 格式化
     * @param e
     * @return
     */
    private String getErrorMsg(Throwable e) {
        if (e == null) {
            return "";
        }
        StackTraceElement[] trace = e.getStackTrace();
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(e);
        for (int i = 0; i < 8; i++) {
            stringBuffer.append("\r\n at " + trace[i]);
        }
        return stringBuffer.toString();
    }

    /**
     * 持久化接口异常日志
     * @param joinPoint
     * @param e
     */
    private void logNote(JoinPoint joinPoint, Throwable e) {
        logNote(joinPoint, null, e);
    }

    /**
     * 持久化接口运行日志
     * @param joinPoint
     * @param data
     */
    private void logNote(JoinPoint joinPoint, Object data) {
        logNote(joinPoint, data, null);
    }

}
